//--------------------------------------------------------------------------------
/*!
\file	SoundBuffer.cpp
\author	bsmith, dkarp
\date	02/21/07
\brief	Polymorphic Sound Buffer types.
\par	license
Copyright (c) DigiPen Institute of Technology 2006. All Rights Reserved. 
*/
//--------------------------------------------------------------------------------

#include "stdafx.h"
#include "LowLevel.h"

using namespace sndcore;
//-------------------------------------------------------------------------------
/*!
\brief  Gets size of buffer
\return size of buffer
\author Danny Karp
*/
//-------------------------------------------------------------------------------
DWORD SoundBufferDS::GetSize() const
{
	DSBCAPS caps;
	caps.dwSize = sizeof(  caps );
	HRESULT hr = pBuffer_->GetCaps( &caps );
	return FAILED(hr) ? 0 : caps.dwBufferBytes;
}
//-------------------------------------------------------------------------------
/*!
\brief  Gets cursor positions
\param  playCursor
gets play cursor
\param  writeCursor
gets write cursor

\return error code
\author	Danny Karp
*/
//-------------------------------------------------------------------------------
HRESULT SoundBufferDS::GetCursorPosition(DWORD *playCursor, DWORD *writeCursor) const
{
	return pBuffer_->GetCurrentPosition( playCursor, writeCursor );
}
//-------------------------------------------------------------------------------
/*!
\brief  sets cursor positions
\param  playCursor
sets play cursor

\return error if false
\author	Danny Karp
*/
//-------------------------------------------------------------------------------
bool SoundBufferDS::SetCursorPosition(DWORD playCursor)
{
	return SUCCEEDED(pBuffer_->SetCurrentPosition(playCursor));
}
//-------------------------------------------------------------------------------
/*!
\brief  initializes the buffer
\param  buffOptions
buffer options

\return error if false
\author	Danny Karp
*/
//-------------------------------------------------------------------------------
bool SoundBufferDS::Initialize(const SoundBufferOptions& /*buffOptions*/)
{
	//Handled automatically upon creation
	return true;
}
//-------------------------------------------------------------------------------
/*!
\brief  shutdown the buffer

\return error if false
\author	Danny Karp
*/
//-------------------------------------------------------------------------------
bool SoundBufferDS::Shutdown()
{
	SAFE_RELEASE(pBuffer_);
	return true;
}
//-------------------------------------------------------------------------------
/*!
\brief  plays the buffer
\param  flags
play flags

\return error if false
\author	Danny Karp
*/
//-------------------------------------------------------------------------------
bool SoundBufferDS::Play(SB_PLAY_FLAGS flags)
{
	return SUCCEEDED(pBuffer_->Play(0, 0, flags));
}
//-------------------------------------------------------------------------------
/*!
\brief  stops the buffer

\return error if false
\author	Danny Karp
*/
//-------------------------------------------------------------------------------
bool SoundBufferDS::Stop()
{
	return SUCCEEDED(pBuffer_->Stop());
}
//-------------------------------------------------------------------------------
/*!
\brief  write buffer
\param  size
 size to write
\param  data
buffer data to write
\param  offset
offset in buffer to write from

\return error if false
\author	Danny Karp
*/
//-------------------------------------------------------------------------------
bool SoundBufferDS::WriteBuffer(DWORD size, LPVOID data, DWORD offset )
{     
	//safety check
	assert( size > 0  );
	
	  //Lock buffer
	BYTE* buffPtr[2] = {0};
	DWORD buffSize[2] = {0};

	HRESULT hr = pBuffer_->Lock(offset, size, (LPVOID*)(&buffPtr[0]), &buffSize[0],
		(LPVOID*)(&buffPtr[1]), &buffSize[1], 0 );//DSBLOCK_FROMWRITECURSOR );

 
    // If DSERR_BUFFERLOST is returned, restore and retry lock.  
    if(  hr == DSERR_BUFFERLOST ) 
    { 
        hr = pBuffer_->Restore(); 
		hr = pBuffer_->Lock(offset, size, (LPVOID*)(&buffPtr[0]), &buffSize[0],
		(LPVOID*)(&buffPtr[1]), &buffSize[1], 0);//DSBLOCK_FROMWRITECURSOR );
    } 
    if( SUCCEEDED(hr) ) 
    { 
		BYTE * byte_data = reinterpret_cast<BYTE *>(data);
          // Mix first buffer. 
		unsigned int buff_ptr_1 = 0;
		for(; buff_ptr_1 < buffSize[0]; ++buff_ptr_1  )
		{
			buffPtr[0][ buff_ptr_1 ] = byte_data[ buff_ptr_1 ];
		}
		  //Mix second buffer
		for( unsigned int buff_ptr_2 = 0; buff_ptr_2 < buffSize[1]; ++buff_ptr_2 )
		{
			buffPtr[1][ buff_ptr_2 ] = byte_data[ buff_ptr_2+buff_ptr_1 ];
		}

        // Release the data back to DirectSound. 
        hr = pBuffer_->Unlock( buffPtr[0], buffSize[0], 
                                  buffPtr[1], buffSize[1]); 
        if( SUCCEEDED( hr ) ) 
        { 
            return true; 
        } 
	}
	return false;
}
//-------------------------------------------------------------------------------
/*!
\brief  read buffer
\param  offset
offset in buffer to read from
\param  size
size to read
\param  data
returned data

\return error if false
\author	Danny Karp
*/
//-------------------------------------------------------------------------------
bool SoundBufferDS::ReadBuffer(DWORD offset, DWORD size, LPVOID* data) const
{     //safety check
	assert( size > 0 );

	  //Lock buffer
	BYTE* buffPtr[2] = {0};
	DWORD buffSize[2] = {0};
	HRESULT hr = pBuffer_->Lock( offset, size, (LPVOID*)(&buffPtr[0]), &buffSize[0],
		(LPVOID*)(&buffPtr[1]), &buffSize[1], DSBLOCK_FROMWRITECURSOR );

 
    // If DSERR_BUFFERLOST is returned, restore and retry lock.  
    if(  hr == DSERR_BUFFERLOST ) 
    { 
        pBuffer_->Restore(); 
		hr = pBuffer_->Lock( offset, size, (LPVOID*)(&buffPtr[0]), &buffSize[0],
		(LPVOID*)(&buffPtr[1]), &buffSize[1], 0 );
    } 
    if( SUCCEEDED(hr) ) 
    { 
          // Read first portion. 
		for( unsigned int buff_ptr_1 = 0; buff_ptr_1 < buffSize[0]; ++buff_ptr_1 )
		{
			reinterpret_cast<BYTE*>(data)[ buff_ptr_1 ] = buffPtr[0][ buff_ptr_1 ]; 
		}
		  // Read second portion
		for( unsigned int buff_ptr_2 = buffSize[0]; buff_ptr_2 < buffSize[1]; ++buff_ptr_2 )
		{
			reinterpret_cast<BYTE*>(data)[ buff_ptr_2 ] = buffPtr[1][ buff_ptr_2 - buffSize[0] ];
		}

		// Release the data back to DirectSound. 
		hr = pBuffer_->Unlock( buffPtr[0], buffSize[0], 
							   buffPtr[1], buffSize[1]); 
        if( SUCCEEDED( hr ) ) 
        { 
            return true; 
        } 
	}
	return false;
}
//-------------------------------------------------------------------------------
/*!
\brief  init user buffer
\param  buffOptions
buffer create options


\return error if false
\author	Ben Smith
*/
//-------------------------------------------------------------------------------
bool SoundBufferUser::Initialize(const SoundBufferOptions& buffOptions)
{
	options_ = buffOptions;
	buffer_ = new BYTE[buffOptions.size_];
	return true;
}
//-------------------------------------------------------------------------------
/*!
\brief  shutdown user buffer



\return error if false
\author	Ben Smith
*/
//-------------------------------------------------------------------------------
bool SoundBufferUser::Shutdown()
{
	SAFE_DELETE_ARRAY(buffer_);
	return true;
}

//-------------------------------------------------------------------------------
/*!
\brief  write user buffer
\param  size
size to write
\param  data
data to write

\param  offset
offset in buffer to write

\return error if false
\author	Ben Smith
*/
//-------------------------------------------------------------------------------
bool SoundBufferUser::WriteBuffer(DWORD size, LPVOID data, DWORD offset )
{
	//safety check
	assert( size > 0 && size <= options_.size_ );
	if (!( size > 0 && size <= options_.size_ ))
		return false;

	BYTE* bBuff = reinterpret_cast<BYTE*>(buffer_);
	BYTE* bData = reinterpret_cast<BYTE*>(data);


	unsigned writeSpace = options_.size_-offset;
	if (size <= writeSpace) {
		memcpy(&bBuff[offset], data, size);				//Mid Fill
	}
	else {
		memcpy(&bBuff[offset],data,writeSpace);			//End Fill
		memcpy(&bBuff, &bData[writeSpace], size-writeSpace);	//Front Fill
	}
	return true;
}
//-------------------------------------------------------------------------------
/*!
\brief  read user buffer
\param  offset
offset in buffer to read
\param  size
size to read

\param  data
data to return

\return error if false
\author	Ben Smith
*/
//-------------------------------------------------------------------------------
bool SoundBufferUser::ReadBuffer(DWORD offset, DWORD size, LPVOID *data) const
{
	//safety check
	assert(size > 0 && (size+offset) <= options_.size_);
	if(!(size > 0 && (size+offset) <= options_.size_))
		return false;

	BYTE* bBuff = reinterpret_cast<BYTE*>(buffer_);
	BYTE* bData = reinterpret_cast<BYTE*>(data);

	unsigned readSpace = options_.size_-offset;
	if (size <= readSpace) {
		memcpy(data, &bBuff[offset], size);					//Mid Read
	}
	else {
		memcpy(data,&bBuff[offset],readSpace);				//End Read
		memcpy(&bData[readSpace],buffer_,size-readSpace);		//Front Read
	}
	return true;
}
//-------------------------------------------------------------------------------
/*!
\brief  directly reads the buffer
\param  size
size of buffer
\param  size


\return buffer start ptr
\author	Ben Smith
*/
//-------------------------------------------------------------------------------
LPVOID SoundBufferUser::DirectRead(DWORD* size)
{
	if (size)
		*size = options_.size_;
	return buffer_;
}
